package cmmTB;




import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.*;
import java.io.*;

import server.IServerApplication;
import server.Logger;
import server.SAExn_TaskIdAlreadyPresent;
import server.SAExn_UnexpectedTaskId;
import server.SAGenTaskContext;
import server.SAInitTaskContext;
import server.SAIncTaskContext;
import server.ServerApplication;

import cmm.CMMExn_ClusterErr;
import cmm.CMMExn_TaskErr;
import cmm.ClusterManagementModule;
import cmm.ConnectionInformation;


public class CMMTestBench extends ServerApplication implements IServerApplication {

	@SuppressWarnings("unused")
	private static CMMTestBench TB;
	
	private static Logger logger;
	
	private static ClusterManagementModule CMM;
	
	private static Random random;
	
	private static int TaskIDRange;
	
	private static String NetworkConnectionName;
	
//  ----------------------------------------------------------------------------------
	
	private static TBSATaskContext RandomizeNewTask() throws UnknownHostException{
		
		TBSATaskContext context = new TBSATaskContext();

		// Randomize Connection Information
		InetAddress ip = InetAddress.getLocalHost();
		Integer PortVar = random.nextInt(100);
		short port = (short) (8000 + PortVar.shortValue());
		ConnectionInformation connInfo = new ConnectionInformation();
		connInfo.putIP(ip);
		connInfo.putPort(port);
		context.setConnInfo(connInfo);
		// Randomize file number
		context.setFileNumber(new Integer(random.nextInt(TaskIDRange)));
		
		context.setBlockNumber(new Integer(0));
		
		logger.AddEntry("TestBench", "Test - Randomized New Task: Port=" + port + 
				" FileNumber=" + context.getFileNumber());
		
		return context;
	}
	
//	----------------------------------------------------------------------------------	
	
	public CMMTestBench() throws Exception{
		
		logger = new Logger();
		logger.OpenLog("CMMTestBenchLog.txt");
		logger.AddEntry("TestBench", "Info - Initializing test bench.");
		
		InetAddress ip = InetAddress.getLocalHost();
		short port = 8382;
		String ProxyIP = "132.68.52.125";
		short ProxyPort = 8383;
		String DefaultGatewayIP = "132.68.52.254";
		
		ConnectionInformation ConnInfo = new ConnectionInformation();
		ConnInfo.putIP(ip);
		ConnInfo.putPort(port);
		
		ConnectionInformation ProxyConnInfo = new ConnectionInformation();
		ProxyConnInfo.putIP(InetAddress.getByName(ProxyIP));
		ProxyConnInfo.putPort(ProxyPort);
		
		try {
			CMM = new ClusterManagementModule(this, ConnInfo, ProxyConnInfo, "CMM_Group", DefaultGatewayIP, "255.255.255.0", NetworkConnectionName, "132.68.1.2", true);
		} catch (Exception e) {
			logger.AddEntry("TestBench", "Error - Failed to initialize the CMM.");
			throw e;
		}
		
		logger.AddEntry("TestBench", "Info - Test Bench Initialized.");
	}
	
	public void AddLogEntry(String ModuleName, String Entry) {
		logger.AddEntry(ModuleName, Entry);
	}

	public void ReceiveNewTaskId(Integer TaskId) throws SAExn_UnexpectedTaskId,
			SAExn_TaskIdAlreadyPresent {
		logger.AddEntry("TestBench", "Test - Received a new task ID from the CMM - " + TaskId);
	}

	public TBSATaskContext ReconstructTaskContext(SAGenTaskContext OldContext,
			SAGenTaskContext Delta) {
	
		TBSATaskContext OldCtx = (TBSATaskContext)OldContext;
		logger.AddEntry("TestBench", "Test - ReconstructTaskContext method was called.");
		logger.AddEntry("TestBench", "Test - Old context fields:  Port=" + OldCtx.getConnInfo().getPort() +
				" FileID=" + OldCtx.getFileNumber() + " Progress=" + OldCtx.getBlockNumber());
		logger.AddEntry("TestBench", "Test - Progress update is " + ((TBSAIncramentalTaskContext)Delta).ProgressUpdate);
		
		TBSATaskContext context = new TBSATaskContext();
		context.setConnInfo(OldCtx.getConnInfo());
		context.setFileNumber(OldCtx.getFileNumber());
		context.setBlockNumber(OldCtx.getBlockNumber() + ((TBSAIncramentalTaskContext)Delta).ProgressUpdate);
		return context;
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {

		// args[0] should be the simulation seed
		// args[1] should be the number of the requests to the CMM
		// args[2] should be the odds in percentage to get a "New Task" request
		// args[3] should be the odds in percentage to get a "Task Finish" request
		// args[4] should be the range of TaskID starting from 0..
		// args[5] should be the number of requests until the TB stops and waits for Enter
		// args[6] should be the Network Connection Name
		
		NetworkConnectionName = args[6];
		
		try {
			TB = new CMMTestBench();
		} catch (Exception e) {
			logger.AddEntry("TestBench", "Error - Failed to initialize the test bench.");
			logger.CloseLog();
			e.printStackTrace();
			return;
		}
		
		logger.AddEntry("TestBench", "Info - Test parameters: " + args[0] + " " + args[1] + " " + args[2] + " " + args[3] + " " + args[4] + " " + args[5]);
		
		long seed = Long.parseLong(args[0]);
		logger.AddEntry("TestBench", "Info - Starting test with seed " + seed);
		random = new Random(seed);
		
		int MaxReqs = Integer.parseInt(args[1]);
		logger.AddEntry("TestBench", "Info - The Test Bench will send " + MaxReqs + " requests to the CMM.");
		
		int NewTaskOdds = Integer.parseInt(args[2]);
		int TaskFinishOdds = Integer.parseInt(args[3]);
		TaskIDRange = Integer.parseInt(args[4]);
		int PendReqs = Integer.parseInt(args[5]);
		
		int Reqs = 0;
		int choise;
		while(true){
			logger.AddEntry("TestBench", "");
			
			if(Reqs == PendReqs){
				for(int i=60; i>0; i--){
					System.out.println("Testbench will continue in " + i + " seconds.");
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}

			}
			
			choise = random.nextInt(100);
			
			// New Task
			if(choise < NewTaskOdds){

				try {
					logger.AddEntry("TestBench", "Test - Request " + Reqs + " is of type \"New Task\".");
					TBSATaskContext context = RandomizeNewTask();
					CMM.AppNewTask(context);
					// can't send another new task until got back task id
				} catch (UnknownHostException e) {
					e.printStackTrace();
					return;
				} catch (CMMExn_ClusterErr e) {
					logger.AddEntry("TestBench", "Test Error - Failed to execute CMM.NewTask()");
				}
			}
			// Task Finish
			else if(choise <= NewTaskOdds+TaskFinishOdds){
				try {
					Integer TaskID = new Integer(random.nextInt(TaskIDRange));
					logger.AddEntry("TestBench", "Test - Request " + Reqs + " is of type \"Task Finish\" for TaskID=" + TaskID);
					CMM.AppTaskFinish(TaskID);
				} catch (CMMExn_TaskErr e) {
					logger.AddEntry("TestBench", "Test - CMM responded with TaskErr to the \"Task Finish\" request.");
				} catch (CMMExn_ClusterErr e) {
					logger.AddEntry("TestBench", "Test Error - Failed to execute CMM.TaskFinish()");
				}
			}
			// Task Update
			else{
				TBSAIncramentalTaskContext update = new TBSAIncramentalTaskContext();
				update.ProgressUpdate = random.nextInt(10);
				
				Integer TaskID = new Integer(random.nextInt(TaskIDRange));
				
				logger.AddEntry("TestBench", "Test - Request " + Reqs + " is of type \"Task Update\" for TaskID=" + TaskID);
				logger.AddEntry("TestBench", "Test - The progress attribute of the IncramentalTaskContext is " + update.ProgressUpdate);
				
				try {
					CMM.AppTaskContextUpdate(TaskID, update);
				} catch (CMMExn_TaskErr e) {
					logger.AddEntry("TestBench", "Test - CMM responded with TaskErr to the \"Task Update\" request.");
				} catch (CMMExn_ClusterErr e) {
					logger.AddEntry("TestBench", "Test Error - Failed to execute CMM.TaskUpdate()");
				}
			}
			Reqs++;
			if(Reqs >= MaxReqs)
				break;
			
			long SleepTime = random.nextInt(500);
			try {
				Thread.sleep(SleepTime);
			} catch (InterruptedException e) {
			}
		}
		
		
		System.out.println("Testbench will end in 30 seconds.");
		try {
			Thread.sleep(30*1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		CMM.Close();
		
		
		logger.AddEntry("TestBench", "Info - Test finished");
		logger.CloseLog();
		
		System.out.println("Test Finished");
	}

	public void DropNewTask() {
		// This can only be an error
		logger.AddEntry("TestBench", "Error - CMM activated SA.DropNewTask() method");
	}

	public void UnrecoverableClusterError() {
		// TODO Auto-generated method stub
		
	}

	public void StartServicingTask(Integer TaskId, SAGenTaskContext Task) {
		// TODO Auto-generated method stub
		
	}
	
	public void StopServicingTask(Integer TaskId) {
		// TODO Auto-generated method stub

	}

}
